package com.nonagon.melladesk.utils;

import com.nonagon.melladesk.vo.UserDetail;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Date;

/**
 * jwt工具类
 * @ClassName:  JWTUtil
 * @author: flitsneak nonagon
 * @date:   2020-12-1
 */
@Component
public class JwtUtil {

    /**
     * token加密时使用的密钥
     * 一旦得到该密钥也就可以伪造token了
     */
    public static String sercetKey = "MelladeskFlitSneakNonagonTech2021";
    /**
     * 代表token的有效时间,设置为7天，不定时刷新。
     */
    public final static long keeptime = 1800000*48*7;
    //jwt前缀
    private final static String TOKEN_PREFIX = "Bearer";

    private final static String authorities = "authorities";


    /**
     * JWT由3个部分组成,分别是 头部Header,载荷Payload一般是用户信息和声明,签证Signature一般是密钥和签名
     * 当头部用base64进行编码后一般都会呈现eyJ...形式,而载荷为非强制使用,签证则包含了哈希算法加密后的数据,包括转码后的header,payload和sercetKey
     * 而payload又包含几个部分,issuer签发者,subject面向用户,iat签发时间,exp过期时间,aud接收方。
     * @Title: generateToken
     * @param: @param id 用户id
     * @param: @param subject 一般用户名，mella客户端使用email。
     * @param: @return
     * @return: String
     * @throws
     */
    public static String generateToken(UserDetail userDetail) {
        long ttlMillis = keeptime;
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        //使用Hash256算法进行加密
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        //获取系统时间以便设置token有效时间
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(sercetKey);
        //将密钥转码为base64形式,再转为字节码
        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
        //对其使用Hash256进行加密
        JwtBuilder builder = Jwts.builder().setId(userDetail.getUserId()).setIssuedAt(now).setIssuer("flitsneak");
        //设计权限
        builder.claim(authorities,userDetail.getAuthorities());
        //JWT生成类,此时设置iat,以及根据传入的id设置token
        if (userDetail.getEmail() != null) {
            builder.setSubject(userDetail.getEmail());
        }
        //由于Payload是非必须加入的,所以这时候要加入检测
        builder.signWith(signatureAlgorithm, signingKey);
        //进行签名,生成Signature
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp);
        }
        //返回最终的token结果
        return builder.compact();
    }
    /**
     * 该函数用于更新token
     * @Title: updateToken
     * @param: @param token
     * @param: @return
     * @return: String
     * @throws
     */
    public static String updateToken(String token) {
        //Claims就是包含了我们的Payload信息类
        UserDetail userDetail = new UserDetail();
        Claims claims = parseToken(token);
        String id = claims.getId();
        String subject = claims.getSubject();
        userDetail.setUserId(id);
        userDetail.setEmail(subject);
        //生成新的token,根据现在的时间
        return generateToken(userDetail);
    }
    /**
     * 将token解密出来,将payload信息包装成Claims类返回
     * @Title: parseToken
     * @param: @param token
     * @param: @return
     * @return: Claims
     * @throws
     */
    public static Claims parseToken(String token) {
        Claims claims = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(sercetKey))
                .parseClaimsJws(token).getBody();
        return claims;
    }

    public String getTokenPrefix() {
        return TOKEN_PREFIX;
    }
    public String getAuthoritiesTag(){
        return authorities;
    }
}